import { initState } from "./state";
import { compileToFunctions } from "./compiler/index";
import { callHook, mountComponent } from "./lifecycle";
import { mergeOptions, nextTick, $watch } from "../src/util";
import Watcher from "./observe/watcher";

export default function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    // vm.$options = options;
    // 在此之前 Vue.mixin 已经执行了，因此 Vue.options 有值
    vm.$options = mergeOptions(vm.constructor.options, options); // vm.constructor.options 即 Vue.options

    log("vm.$options----", vm.$options);
    callHook(vm, "beforeCreate");
    initState(vm); // 初始化状态
    callHook(vm, "created");
    vm.$options.el && vm.$mount(vm.$options.el); // 可以挂载到页面上了
    callHook(vm, "mounted");
  };

  Vue.prototype.$nextTick = nextTick;

  Vue.prototype.$mount = function (el) {
    el = typeof el === "string" ? document.querySelector(el) : el; // 可以是 template 标签
    const vm = this;
    const options = vm.$options;
    el && (vm.$el = el);
    // 1.有render 就用render;
    // 2.无render 就找 template , 变成 render
    // 3.无 template, 就到找外部模板， 如：dom 中的 app
    // 4.使用 render
    if (!options.render) {
      let template = options.template;
      if (!template && el) {
        template = el.outerHTML; // outerHTML 如果不兼容，就用别的办法处理
      }
      options.render = compileToFunctions(template); // 编译的核心方法
      log("render----", options.render);
    }

    mountComponent(vm); // 挂载组件
  };

  // 只给 watch 中使用， 有 user 时， 为非渲染 watcher 使用
  Vue.prototype.$watch = function (exprOrFn, handler, options) {
    let watcher = new Watcher(this, exprOrFn, handler, {
      ...options,
      user: true,
    });
    if (options.immediate) {
      handler();
    }
  };
}
